<?php

/*
 * 宝付支付
 * @陈聪
 */

namespace Service\Pay\Baofoo;

defined('THINK_PATH') or exit();
define ( "BAOFOO_ENCRYPT_LEN", 32 );//定义长度

use Service\Pay;

class Baofoo extends Pay {

    private $private_key;//私钥
    private $public_key;//公钥
    protected $errCode=array(
        'BF00100'=>'系统异常',
        'BF00101'=>'您的身份信息有误,请确认',
        'BF00102'=>'您的银行卡已过期',
        'BF00103'=>'您的账户余额不足',
        'BF00104'=>'您的交易金额超过该银行限制金额',
        'BF00105'=>'短信验证码错误',
        'BF00106'=>'短信验证码失效',
        'BF00107'=>'您还未开通网上支付业务',
        'BF00108'=>'因为银行问题，导致交易失败',
        'BF00109'=>'交易金额低于银行最低支付限制',
        'BF00110'=>'该卡暂不支持此交易异常',
        'BF00111'=>'交易失败',
        'BF00112'=>'系统繁忙，请稍后再试',
        'BF00113'=>'交易结果未知，请稍后查询',
        'BF00114'=>'订单已支付成功，请勿重复支付',
        'BF00115'=>'交易处理中，请稍后查询',
        'BF00116'=>'交易失败',
        'BF00117'=>'交易金额超限,请联系客服',
        'BF00118'=>'交易失败',
        'BF00119'=>'短信验证超时，请稍后再试',
        'BF00120'=>'绑定信息不能为空',
        'BF00121'=>'绑定信息输入有误',
        'BF00122'=>'交易失败',
        'BF00123'=>'交易失败',
        'BF00124'=>'交易失败',
        'BF00125'=>'交易失败',
        'BF00126'=>'该笔订单不存在',
        'BF00127'=>'交易失败',
        'BF00128'=>'该笔订单不存在',
        'BF00129'=>'该笔订单不存在',
        'BF00130'=>'请输入验证码',
        'BF00131'=>'短信验证失败',
        'BF00132'=>'短信验证超时，请稍后再试',
        'BF00133'=>'短信验证失败',
        'BF00134'=>'您未绑定银行卡',
        'BF00135'=>'交易金额不正确',
        'BF00136'=>'订单创建失败',
        'BF00137'=>'交易失败',
        'BF00138'=>'交易失败',
        'BF00140'=>'该卡已被注销',
        'BF00141'=>'该卡已挂失',
        'BF00142'=>'暂不支持该银行卡的绑卡',
        'BF00143'=>'绑卡失败',
        'BF00144'=>'订单已提交处理中',
        'BF00146'=>'订单金额超过单笔限额',
        'BF00147'=>'该银行卡不支持此交易',
        'BF00177'=>'交易失败',
        'BF00180'=>'获取短信验证码失败',
        'BF00182'=>'您输入的银行卡号有误，请重新输入',
        'BF00186'=>'该卡已绑定',
        'BF00187'=>'暂不支持信用卡的绑定',
        'BF00188'=>'绑卡失败',
        'BF00189'=>'交易金额超过限额',
        'BF00190'=>'交易失败',
        'BF00191'=>'交易失败',
        'BF00192'=>'交易失败',
        'BF00193'=>'交易失败',
        'BF00194'=>'交易失败',
        'BF00195'=>'交易金额不正确',
        'BF00196'=>'金额不正确',
        'BF00197'=>'还款总金额不正确',
        'BF00198'=>'交易失败',
        'BF00199'=>'交易失败',
        'BF00200'=>'订单不一致',
        'BF00201'=>'发送短信和支付交易时金额不相等',
        'BF00202'=>'交易超时，请稍后查询',
        'BF00203'=>'退款交易已受理',
        'BF00204'=>'交易失败',
        'BF00232'=>'银行卡未开通认证支付',
        'BF00233'=>'密码输入次数超限',
        'BF00234'=>'单日交易金额超限',
        'BF00235'=>'单笔交易金额超限',
        'BF00236'=>'卡号无效，请确认后输入',
        'BF00237'=>'该卡已冻结',
        'BF00238'=>'已交易，请稍后查询',
        'BF00309'=>'绑卡和发送短信时手机号不一致',
        'BF00311'=>'暂不支持该卡类型',
        'BF00312'=>'请确认卡号是否输错',
        'BF00313'=>'交易失败',
        'BF00315'=>'手机号码为空，请重新输入'
    );

    function __construct($options=array()) {
       if(empty($options)) {
            $pay_config = C('PAY_CONFIG');
            $options = isset($pay_config['Baofoo'])?$pay_config['Baofoo']:array();
        }
        $this->options = $options;
        // 初始化商户私钥
        $pkcs12 = file_get_contents ($this->options['private_key_path']);
        $private_key_data = array ();
        openssl_pkcs12_read ( $pkcs12, $private_key_data, $this->options['private_key_password'] );
        $this->private_key = $private_key_data ["pkey"];
        // 宝付公钥
        $keyFile = file_get_contents ($this->options['public_key_path']);
        $this->public_key = openssl_get_publickey ( $keyFile );
    }

    /**
     * @desc 预绑定
     * @author 陈聪
     * @param array $data
     *        user_id:用户ID
     *        order_sn:订单编号
     *        bank_card_number:银行卡号
     *        id_card:身份证号码
     *        card_name:持卡人姓名
     *        mobile:预留手机号码
     *        bank_code:宝付银行编码
     */
    public function preBind($data){
        $rts=array();
        if (!isset($data['order_sn'])) {
            $rts['rt']=-1;
            $rts['msg']="订单号不能为空！";
            return $rts;
        }
        //================报文组装=================================
        $parms = array();
        $parms["txn_sub_type"]=11;//交易子类
        $parms["biz_type"]="0000"; //接入类型
        $parms["terminal_id"]=$this->options['terminal_id'];
        $parms["member_id"]= $this->options['mch_id'];//商户订单编号
        $parms["trans_serial_no"]=create_order_sn(99,'BFPB');//商户流水号,8-20 位字母和数字，每次请求都不可重复(当天和历史均不可重复)
        $parms["trans_id"] = $data['order_sn'];//商户订单编号,唯一订单号，8-20 位字母和数字，同一天内不可重复；该订单号从发送短信类交易到当前交易都有效
        $parms["acc_no"] = $data['bank_card_number']; //银行卡卡号
        $parms["id_card_type"] = "01";//证件类型固定01（身份证）
        $parms["id_card"] = $data['id_card']; //身份证号码
        $parms["id_holder"] =  $data['card_name']; //姓名
        $parms["mobile"] = $data['mobile']; //银行预留手机号
        $parms["valid_date"] = "";//卡有效期 （传空）
        $parms["valid_no"] = ""; //卡安全码（传空）
        $parms["pay_code"] = $data['bank_code']; //银行卡编码
        $parms["trade_date"]=date('YmdHis'); //订单日期 YmdHis
        $parms["additional_info"]=isset($data['user_id'])?$data['user_id']:""; //附加字段  为用户id
        $parms["req_reserved"]= isset($data['user_id'])?$data['user_id']:""; //保留
        $rts=$this->_callBaofu($this->options['request_url'],$parms);
        $rts['data']=array();
        if($rts['rt']==1 && isset($rts['return_data']['trans_id'])){
            $rts['data']['trans_id']=$rts['return_data']['trans_id'];
        }
        return $rts;
    }

    /**
     * @desc 绑定
     * @author 陈聪
     * @param array $data
     *        user_id:用户ID
     *        order_sn:订单编号
     *        sms_code:短信验证码
     */
    public function bind($data){
        $rts=array();
        if (!isset($data['trans_id'])) {
            $rts['rt']=-1;
            $rts['msg']='订单号不能为空！';
            return $rts;
        }
        if (!isset($data['sms_code'])) {
            $rts['rt']=-2;
            $rts['msg']="短信验证码不能为空！";
            return $rts;
        }
        $parms = array();
        $parms["txn_sub_type"]=12;
        $parms["biz_type"]="0000"; //接入类型
        $parms["terminal_id"]=$this->options['terminal_id'];
        $parms["member_id"]= $this->options['mch_id'];//商户订单编号
        $parms["trans_serial_no"]= create_order_sn(99,'BFB');//商户流水号,8-20 位字母和数字，每次请求都不可重复(当天和历史均不可重复)
        $parms["trans_id"] = $data['trans_id'];
        $parms["sms_code"] = $data['sms_code'];
        $parms["trade_date"]=date('YmdHis'); //订单日期 YmdHis
        $parms["additional_info"]=isset($data['user_id'])?$data['user_id']:""; //附加字段  为用户id
        $parms["req_reserved"]= isset($data['user_id'])?$data['user_id']:""; //保留
        $rts=$this->_callBaofu($this->options['request_url'],$parms);
        $rts['data']=array();
        if($rts['rt']==1 && isset($rts['return_data']['bind_id'])){
            $rts['data']['baofu_bind_id']=$rts['return_data']['bind_id'];
        }
        return $rts;
    }

    /**
     * @desc 查询办卡状态
     * @author 陈聪
     * @param array $data
     *        user_id:用户ID
     *        bank_card_number:银行卡号
     */
    public function queryBindStatus($data){
        $rts=array();
        if (!isset($data['bank_card_number'])) {
            $rts['rt']=-1;
            $rts['msg']='银行卡号不能为空！';
            return $rts;
        }
        $parms = array();
        $parms["txn_sub_type"]='03';
        $parms["biz_type"]="0000"; //接入类型
        $parms["terminal_id"]=$this->options['terminal_id'];
        $parms["member_id"]= $this->options['mch_id'];//商户订单编号
        $parms["trans_serial_no"]= create_order_sn(99,'BFQB');//商户流水号,8-20 位字母和数字，每次请求都不可重复(当天和历史均不可重复)
        $parms["acc_no"] = $data['bank_card_number'];
        $parms["trade_date"]=date('YmdHis'); //订单日期 YmdHis
        $parms["additional_info"]=isset($data['user_id'])?$data['user_id']:""; //附加字段  为用户id
        $parms["req_reserved"]= isset($data['user_id'])?$data['user_id']:""; //保留
        $rts=$this->_callBaofu($this->options['request_url'],$parms);
        $rts['data']=array();
        if($rts['rt']==1 && isset($rts['return_data']['bind_id'])){
            $rts['data']['baofu_bind_id']=$rts['return_data']['bind_id'];
        }
        return $rts;
    }

    /**
     * @desc 解绑
     * @author 陈聪
     * @param array $data
     *        user_id:用户ID
     *        baofu_bind_id:绑定标识
     */
    public function unBind($data){
        $rts=array();
        if (!isset($data['baofu_bind_id'])) {
            $rts['rt']=-1;
            $rts['msg']='绑定标识不能为空！';
            return $rts;
        }
        $parms = array();
        $parms["txn_sub_type"]='02';
        $parms["biz_type"]="0000"; //接入类型
        $parms["terminal_id"]=$this->options['terminal_id'];
        $parms["member_id"]= $this->options['mch_id'];//商户订单编号
        $parms["trans_serial_no"]= create_order_sn(99,'BFUB');//商户流水号,8-20 位字母和数字，每次请求都不可重复(当天和历史均不可重复)
        $parms["bind_id"] = $data['baofu_bind_id'];
        $parms["trade_date"]=date('YmdHis'); //订单日期 YmdHis
        $parms["additional_info"]=isset($data['user_id'])?$data['user_id']:""; //附加字段  为用户id
        $parms["req_reserved"]= isset($data['user_id'])?$data['user_id']:""; //保留
        $rts=$this->_callBaofu($this->options['request_url'],$parms);
        return $rts;
    }

    /**
     * @desc 预支付
     * @author 陈聪
     * @param array $data
     *        user_id:用户ID
     *        order_sn:订单编号
     *        baofu_bind_id:绑定标识
     *        amount:支付金额(以元为单位)
     *        ip:用户访问时的IP
     */
    public function prePay($data){
        $rts=array();
        if (!isset($data['order_sn'])) {
            $rts['rt']=-1;
            $rts['msg']='订单编号不能为空！';
            return $rts;
        }
        if (!isset($data['bind_id'])) {
            $rts['rt']=-2;
            $rts['msg']='绑定标识不能为空！';
            return $rts;
        }
        $amount=floatval($data['amount'])*100;
        if($amount<=0){
            $rts['rt']=-3;
            $rts['msg']='支付金额不正确！';
            return $rts;
        }
        if (!isset($data['ip'])) {
            $rts['rt']=-4;
            $rts['msg']='IP不能为空！';
            return $rts;
        }
        $parms = array();
        $parms["txn_sub_type"]='15';
        $parms["biz_type"]="0000"; //接入类型
        $parms["terminal_id"]=$this->options['terminal_id'];
        $parms["member_id"]= $this->options['mch_id'];//商户订单编号
        $parms["trans_serial_no"]= create_order_sn(99,'BFRP');//商户流水号,8-20 位字母和数字，每次请求都不可重复(当天和历史均不可重复)
        $parms["trans_id"] = $data['order_sn'];
        $parms["bind_id"] = $data['bind_id'];
        $parms["txn_amt"] = $amount;
        $parms["trade_date"]=date('YmdHis'); //订单日期 YmdHis
        $parms["additional_info"]=isset($data['user_id'])?$data['user_id']:""; //附加字段  为用户id
        $parms["req_reserved"]= isset($data['user_id'])?$data['user_id']:""; //保留
        $parms['risk_content']=array("client_ip"=>$data['ip']);//风险控制参数,具体参数如下风险控制参数
        $rts=$this->_callBaofu($this->options['request_url'],$parms);
        return $rts;
    }

    /**
     * @desc 确认付款
     * @author 陈聪
     * @param array $data
     *        user_id:用户ID
     *        business_no:宝付流水号
     *        sms_code:短信验证码
     */
    public function pay($data){
        $rts=array();
        if (!isset($data['business_no'])) {
            $rts['rt']=-1;
            $rts['msg']='宝付流水号不能为空！';
            return $rts;
        }
        if (!isset($data['sms_code'])) {
            $rts['rt']=-2;
            $rts['msg']='短信验证码不能为空！';
            return $rts;
        }
        $parms = array();
        $parms["txn_sub_type"]='16';
        $parms["biz_type"]="0000"; //接入类型
        $parms["terminal_id"]=$this->options['terminal_id'];
        $parms["member_id"]= $this->options['mch_id'];//商户订单编号
        $parms["trans_serial_no"]= create_order_sn(99,'BFP');//商户流水号,8-20 位字母和数字，每次请求都不可重复(当天和历史均不可重复)
        $parms["business_no"] = $data['business_no'];//宝付业务流水号,	由宝付返回，用于交易中唯一标识一笔交易
        $parms["sms_code"] = $data['sms_code'];
        $parms["trade_date"]=date('YmdHis'); //订单日期 YmdHis
        $parms["additional_info"]=isset($data['user_id'])?$data['user_id']:""; //附加字段  为用户id
        $parms["req_reserved"]= isset($data['user_id'])?$data['user_id']:""; //保留
        $rts=$this->_callBaofu($this->options['request_url'],$parms);
        return $rts;
    }

    /**
     * @desc 付款订单状态查询
     * @author 陈聪
     * @param array $data
     *        user_id:用户ID
     *        orig_trans_id:宝付流水号
     */
    public function queryPayStatus($data){
        $rts=array();
        if (!isset($data['order_sn'])) {
            $rts['rt']=-1;
            $rts['msg']='宝付流水号不能为空！';
            return $rts;
        }
        $parms = array();
        $parms["txn_sub_type"]='06';//	取值:14或15
        $parms["biz_type"]="0000"; //接入类型
        $parms["terminal_id"]=$this->options['terminal_id'];
        $parms["member_id"]= $this->options['mch_id'];//商户订单编号
        $parms["trans_serial_no"]= create_order_sn(99,'BFQP');//商户流水号,8-20 位字母和数字，每次请求都不可重复(当天和历史均不可重复)
        //orig_trans_id 需要跟宝付的人确认是哪个接口返回的哪个字段
        $parms["orig_trans_id"] = $data['order_sn'];//由宝付返回，用于在后续类交易中唯一标识一笔交易
        $parms["trade_date"]=date('YmdHis'); //订单日期 YmdHis
        $parms["additional_info"]=isset($data['user_id'])?$data['user_id']:""; //附加字段  为用户id
        $parms["req_reserved"]= isset($data['user_id'])?$data['user_id']:""; //保留
        $rts=$this->_callBaofu($this->options['request_url'],$parms);
        return $rts;
    }

    /**
     * @desc 向宝付请求数据
     * @author 陈聪
     * @param string $url 请求地址
     * @param array $param 参数集合
     * @return array 返回集合
     */
    private function _callBaofu($url,$param){
        $rts=array();
        if ($this->options['data_type'] == "json") {
            $Encrypted_string = str_replace("\\/", "/", json_encode($param)); //转JSON
        } else {
            $Encrypted_string = xml_encode($param,'data_content'); //转XML
        }
        $rts['is_query']=0;//因交易失败,需调用宝付查询接口
        $Encrypted = $this->encryptedByPrivateKey($Encrypted_string); //先BASE64进行编码再RSA加密
        $PostArry = array(
            "version" => $this->options['version'],
            "terminal_id" => $this->options['terminal_id'],
            "txn_type" => $this->options['txn_type'],
            "txn_sub_type" => $param['txn_sub_type'],
            "member_id" => $this->options['mch_id'],
            "data_type" => $this->options['data_type'],
            "data_content" => $Encrypted
        );
        //发送请求到宝付服务器，并输出返回结果
        $return=$this->curl_post($url, http_build_query($PostArry));
        if (empty($return)) {
            //记录日志
            third_party_api_log($url,$param,$return,$this->options['type']);
            $rts['rt']=-1;
            $rts['msg']='请求失败';
            $rts['return_msg'] = '返回结果为空,请重试!';
            return $rts;
        }
        $return_decode = $this->decryptByPublicKey($return); //解密返回的报文
        $endata_content = array();
        if (!empty($return_decode)) {//解析XML、JSON
            if ($this->options['data_type'] == "xml") {
                $endata_content = json_decode(json_encode((array)simplexml_load_string($return_decode)), true);
            } else {
                $endata_content = json_decode($return_decode, TRUE);
            }
            $rts['return_json']=  json_encode($endata_content);//保存接口返回数据
            if (is_array($endata_content) && (count($endata_content) > 0)) {
                if (array_key_exists("resp_code", $endata_content)) {
                    if ($endata_content["resp_code"] == "0000") {
                        $rts['rt']=1;
                        $rts['msg']='请求成功!';
                        $rts['return_data']=$endata_content;//接口返回的数据
                    } else {
                        $query_no_arr=array('BF00100','BF00112','BF00113','BF00115','BF00144','BF00202','BF00254','BF00238');
                        if(in_array($endata_content['resp_code'],$query_no_arr)){
                            $rts['is_query']=1;//需调用宝付查询接口
                        }
                        //错误或失败其他状态
                        $rts['rt']=0;
                        if(array_key_exists($endata_content['resp_code'],$this->errCode)){
                            $rts['msg']=$this->errCode[$endata_content['resp_code']];
                        }else{
                            $rts['msg']=$endata_content['resp_msg'];
                        }
                    }
                } else {
                    $rts['rt']=-2;
                    $rts['msg']='请求失败！';
                }
            }else{
                //记录日志 返回的数据及解密后的数据
                third_party_api_log($url,$param,$rts,$this->options['type']);
                $rts['rt']=-3;
                $rts['msg']='请求失败!';
                return $rts;
            }
        } else {
            //记录日志 返回的数据及解密后的数据
            third_party_api_log($url,$param,$rts,$this->options['type']);
            $rts['rt']=-4;
            $rts['msg']='请求失败!';
            return $rts;
        }
        //记录日志 返回的数据及解密后的数据
        third_party_api_log($url,$param,$rts,$this->options['type']);
        return $rts;
    }

    /**
     * @desc 私钥加密
     * @author 刘冰冰
     * @date 2016年7月20日
     * @param array $data_content
     * @return string
     */
    public function encryptedByPrivateKey($data_content) {
        $data_content = base64_encode ( $data_content );
        $encrypted = "";
        $totalLen = strlen ( $data_content );
        $encryptPos = 0;
        while ( $encryptPos < $totalLen ) {
            openssl_private_encrypt ( substr ( $data_content, $encryptPos, BAOFOO_ENCRYPT_LEN ), $encryptData, $this->private_key );
            $encrypted .= bin2hex ( $encryptData );
            $encryptPos += BAOFOO_ENCRYPT_LEN;
        }
        return $encrypted;
    }

    /**
     * @desc 公钥解密
     * @author 刘冰冰
     * @date 2016年7月20日
     * @param string $encrypted
     * @return string
     */
    public function decryptByPublicKey($encrypted) {
        $decrypt = "";
        $totalLen = strlen ( $encrypted );
        $decryptPos = 0;
        while ( $decryptPos < $totalLen ) {
            openssl_public_decrypt ( hex2bin ( substr ( $encrypted, $decryptPos, BAOFOO_ENCRYPT_LEN * 8 ) ), $decryptData, $this->public_key );
            $decrypt .= $decryptData;
            $decryptPos += BAOFOO_ENCRYPT_LEN * 8;
        }
        $decrypt = base64_decode ( $decrypt );
        return $decrypt;
    }

}
